package com.yootoo.extpmml.dsl

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.alibaba.fastjson.serializer.SerializerFeature
import groovy.util.logging.Slf4j
//import me.zzp.am.Am
import okhttp3.FormBody
import okhttp3.HttpUrl
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import org.jboss.logging.MDC
import redis.clients.jedis.commands.JedisCommands

@Slf4j
class AdapterContext {
    /**
     * 即adapterId
     */
    private String name
    /**
     * 全局变量
     */
    def global
    /**
     * 输入变量，为controller入参
     */
    def input
    /**
     * callApi时的RequestBody
     */
    def body
    /**
     * callApi后响应体，code为http code，body为响应string
     */
    def response = [:]
    /**
     * 数据库代理，使用db.${name}获取的为Ao对象
     */
//    static Am db
    /**
     * redis代理
     */
    static JedisCommands redis
    static Map<String, AdapterExecutor> executors
    Map<String, AdapterRequest> api

    final static int SMG3_RESULT_CODE = 301

    static {
        String.metaClass.asJSON = { -> return JSON.parseObject(delegate as String) }
        Map.metaClass.toJSONString() { ->
            return JSON.toJSONString(delegate, SerializerFeature.DisableCircularReferenceDetect)
        }
        String.metaClass.asXmlString = { -> new XmlSlurper().parseText(delegate as String).text() }
        String.metaClass.decrypt = { -> kmsHelper.decode(delegate as String) }
        String.metaClass.encrypt = { -> kmsHelper.encode(delegate as String) }
    }

    def ctx = [:]
    OkHttpClient client

/**
 * 抛出静默异常，此异常触发告警
 */
    def fail() {
        throw new Exception(name)
    }

    /**
     * 抛出静默异常，此异常触发告警
     * @param message 异常信息
     */
    def fail(String message) {
        throw new Exception(name, message)
    }

    /**
     * 抛出静默异常，此异常不触发告警
     */
    def ignore() {
        throw new Exception(name)
    }

    /**
     * 抛出静默异常，此异常不触发告警
     * @param message 异常信息
     */
    def ignore(String message) {
        throw new Exception(name, message)
    }



    /**
     * 调用其他adapter
     * @param adapterName适配器名称
     * @return 该adapter的执行结果
     */
    def require(String adapterName) {
        require(adapterName, input)
    }

    /**
     * 调用其他adapter
     * @param adapterName适配器名称
     * @param 指定入参 /上下文，调用的adapter都从input中取参数
     * @return 该adapter的执行结果
     */
    def require(String adapterName, Map<String, Object> customInput) {
        executors.get(adapterName).doAdapter(customInput)
    }

//    static void setDb(Am am) {
//        db = am
//        db.metaClass.propertyMissing = { name ->
//            delegate.getAo(name)
//        }
//    }

    /**
     * 调用API 结果在response中，code为http的code body为响应string, 非2xx请求会直接抛出RpcException，SMG3返回码不为301会抛出异常
     * @param apiKey api名称，对应given中定义的api的key
     */
    def callSmg3(String apiKey) {
        callRpc(apiKey)
        JSONObject json = response.body.asJSON()
        response.json = json
        def resCode = json.getInteger('result_code')
        if (resCode != SMG3_RESULT_CODE) {
            fail("SMG3返回值[${resCode}]错误: ${response.body}")
        }
    }

    /**
     * 调用API 结果在response中，code为http的code body为响应string, 非2xx请求会直接抛出RpcException
     * @param apiKey api名称，对应given中定义的api的key
     */
    def callRpc(String apiKey) {
        callApi(apiKey)
        int resCode = response.code
        if (!(resCode >= 200 && resCode < 300)) {
            throw new Exception(ErrorConstants.ADAPTER_EXECUTION_FAILED, apiKey, resCode, response.body as String)
        }
    }

    /**
     * 调用API 结果在response中，code为http的code body为响应string
     * @param apiKey api名称，对应given中定义的api的key
     */
    def callApi(String apiKey) {
        AdapterRequest req = api[apiKey]
        // 支持动态url
        def url = req._urlClosure != null ? req._urlClosure.rehydrate(this, this, this)() : req._url

        log.info(apiKey + "--执行-->" + url)
        def urlBuilder = HttpUrl.parse(url).newBuilder()
        def builder = new Request.Builder()
        def contentType = req._header['Content-Type'] as String
        if ('GET'.equalsIgnoreCase(req._method)) {
            if (body) {
                if (body instanceof Map) {
                    body.each { k, v -> urlBuilder.addQueryParameter(k as String, v as String) }
                } else if (body instanceof String || body instanceof GString) {
                    urlBuilder.query(body)
                }
            }
            builder.url(urlBuilder.build()).get()
        } else {
            builder.url(urlBuilder.build())
            def requestBody
            if (contentType?.contains('form') && body instanceof Map) {
                FormBody.Builder form = new FormBody.Builder()
                body.each { name, value -> form.add(name as String, value.toString()) }
                requestBody = form.build()
            } else {
                requestBody = RequestBody.create(
                        MediaType.parse(contentType ? contentType : 'application/json'),
                        body instanceof String || body instanceof GString ? body.bytes : JSON.toJSONBytes(body, SerializerFeature.DisableCircularReferenceDetect))
            }
            builder.method(req._method, requestBody)
        }
        def header = req._headerClosure != null ? req._headerClosure.rehydrate(this, this, this)() : req._header
        header?.each { k, v ->
            builder.header(k, v)
        }
        def ariesId = MDC.get('dt_aries_id')
        if (ariesId) {
            builder.header('Dt-Aries-Id', ariesId)
        }
        def trackId = MDC.get("tid")
        if (trackId) {
            builder.addHeader("X-Tracking-ID", trackId)
        }
        def res = null
        def request = builder.build()
        try {
            if (log.isDebugEnabled()) {
                log.debug("[{}] 发送请求[{}], request=[{}].")
            } else if (req._trace) {
                log.info("[{}] 发送请求[{}], request=[{}].", name, apiKey, request)
            }
            res = client.newCall(request).execute()
            // res = callGlobal(apiKey, request)
            response.code = res.code()
            response.body = res.body().string()
            response.header = request.headers().toString()
            response.url = request.url().toString()
            response.method = request.method()
            if (req._trace || log.isDebugEnabled()) {
                log.info("[{}] 收到响应[{}], code=[{}], body={}.", name, apiKey, response.code, response.body)
            }
            return response
        } catch (Exception e) {
            throw e
        } finally {
            res?.close()
        }
    }
}
